#include "L2CDTPClient.hpp"
#include <string.h>

using namespace L2CDTP;

Client::Client(ReadFrameCallback read_frame_callback,SendCallback send_callback){
    this->read_frame_callback = read_frame_callback;
    this->send_callback = send_callback;
}

Client::~Client(){ }

bool Client::executeCommand(CommandRequest_t request,CommandResponse_t *response){
    if ( read_frame_callback == nullptr ) return false;
    if ( send_callback == nullptr ) return false;
    if ( request.bundle_data == nullptr && request.bundle_data_size > 0 ) return false;
    FrameHeader_t request_header;
    request_header.type = TYPE_COMMAND;
    request_header.flag = 0x00;
    request_header.extra = request.extra_data;
    request_header.label_length = strlen(request.command_label) + 1;
    request_header.data_length = request.bundle_data_size;

    unsigned int request_frame_length = sizeof(FrameHeader_t) + request_header.label_length + request_header.data_length;
    char request_buffer[request_frame_length];
    memset(request_buffer,0,request_frame_length);
    memcpy(request_buffer,&request_header,sizeof(FrameHeader_t));
    strcpy(request_buffer + sizeof(FrameHeader_t),request.command_label);
    memcpy(request_buffer + sizeof(FrameHeader_t) + request_header.label_length,request.bundle_data,request_header.data_length);

    /* Send request header. */
    send_callback(request_buffer,request_frame_length);

    /* Receive response. */
    char response_buffer[MAX_FRAME_SIZE];
    memset(response_buffer,0,MAX_FRAME_SIZE);
    unsigned int response_size = read_frame_callback(response_buffer);
    FrameHeader_t *response_header = (FrameHeader_t*)response_buffer;
    char *label = response_buffer + sizeof(FrameHeader_t);
    char *data = label + response_header->label_length;
    if ( response_size != (sizeof(FrameHeader_t) + response_header->label_length + response_header->data_length) ) return false;
    if ( strcmp(request.command_label,label) != 0 ) return false;
    response->extra_data = response_header->extra;
    response->bundle_data_size = response_header->data_length;
    memcpy(response->bundle_data,data,response_header->data_length);

    if ( response_header->flag & FLAG_SUCCESS ){
        return true;
    }else{
        return false;
    }
}

bool Client::accessData(DataRequest_t request,DataResponse_t *response){
    if ( read_frame_callback == nullptr ) return false;
    if ( send_callback == nullptr ) return false;
    FrameHeader_t request_header;
    request_header.type = TYPE_DATA;
    request_header.flag = request.rw | request.type;
    request_header.extra = request.extra_data;
    request_header.label_length = strlen(request.data_label) + 1;
    request_header.data_length = request.data_size;

    unsigned int request_frame_length = sizeof(FrameHeader_t) + request_header.label_length + request_header.data_length;
    char request_buffer[request_frame_length];
    memset(request_buffer,0,request_frame_length);
    memcpy(request_buffer,&request_header,sizeof(FrameHeader_t));
    strcpy(request_buffer + sizeof(FrameHeader_t),request.data_label);
    memcpy(request_buffer + sizeof(FrameHeader_t) + request_header.label_length,request.data,request.data_size);

    /* Send request header. */
    send_callback(request_buffer,request_frame_length);

    /* Receive response. */
    char response_buffer[MAX_FRAME_SIZE];
    memset(response_buffer,0,MAX_FRAME_SIZE);
    unsigned int response_size = read_frame_callback(response_buffer);
    FrameHeader_t *response_header = (FrameHeader_t*)response_buffer;
    char *label = response_buffer + sizeof(FrameHeader_t);
    char *data = label + response_header->label_length;
    if ( response_size != (sizeof(FrameHeader_t) + response_header->label_length + response_header->data_length) ) return false;
    if ( strcmp(request.data_label,label) != 0 ) return false;
    response->extra_data = response_header->extra;
    response->data_size = response_header->data_length;
    memcpy(response->data,data,response_header->data_length);

    if ( response_header->flag & FLAG_SUCCESS ){
        return true;
    }else{
        return false;
    }
}